<template>
	<transition name="el-zoom-in-top" @after-leave="$emit('dodestroy')">
		<div
			v-show="visible"
			class="el-time-range-picker el-picker-panel el-popper"
			:class="popperClass">
			<div class="el-time-range-picker__content flex">
				<div class="el-time-range-picker__cell">
					<div class="el-time-range-picker__header">
						{{ i18n("el.datepicker.startTime") }}
					</div>
					<div
						:class="{ 'has-seconds': showSeconds, 'is-arrow': arrowControl }"
						class="el-time-range-picker__body el-time-panel__content">
						<BasicTimeSpinner
							ref="minSpinner"
							:show-seconds="showSeconds"
							:am-pm-mode="amPmMode"
							@change="handleMinChange"
							:arrow-control="arrowControl"
							@select-range="setMinSelectionRange"
							:date="minDate">
						</BasicTimeSpinner>
					</div>
				</div>
				<div class="el-time-range-picker__cell">
					<div class="el-time-range-picker__header">
						{{ i18n("el.datepicker.endTime") }}
					</div>
					<div
						:class="{ 'has-seconds': showSeconds, 'is-arrow': arrowControl }"
						class="el-time-range-picker__body el-time-panel__content">
						<BasicTimeSpinner
							ref="maxSpinner"
							:show-seconds="showSeconds"
							:am-pm-mode="amPmMode"
							@change="handleMaxChange"
							:arrow-control="arrowControl"
							@select-range="setMaxSelectionRange"
							:date="maxDate">
						</BasicTimeSpinner>
					</div>
				</div>
			</div>
			<div class="el-time-panel__footer">
				<button type="button" class="el-time-panel__btn cancel" @click="handleCancel()">
					{{ i18n("el.datepicker.cancel") }}
				</button>
				<button
					type="button"
					class="el-time-panel__btn confirm"
					@click="handleConfirm()"
					:disabled="btnDisabled">
					{{ i18n("el.datepicker.confirm") }}
				</button>
			</div>
		</div>
	</transition>
</template>
<script lang="ts">
export default async function () {
	const [{ parseDate, limitTimeRange, modifyDate, clearMilliseconds, timeWithinRange }] =
		await _.$importVue(["/common/ui-x/components/form/xDatePicker/dateUtils.vue"]);

	const MIN_TIME = parseDate("00:00:00", "HH:mm:ss");
	const MAX_TIME = parseDate("23:59:59", "HH:mm:ss");

	const minTimeOfDay = function (date) {
		return modifyDate(MIN_TIME, date.getFullYear(), date.getMonth(), date.getDate());
	};

	const maxTimeOfDay = function (date) {
		return modifyDate(MAX_TIME, date.getFullYear(), date.getMonth(), date.getDate());
	};

	// increase time by amount of milliseconds, but within the range of day
	const advanceTime = function (date, amount) {
		return new Date(Math.min(date.getTime() + amount, maxTimeOfDay(date).getTime()));
	};

	return defineComponent({
		components: {
			BasicTimeSpinner: () =>
				_.$importVue("/common/ui-x/components/form/xDatePicker/basic/BasicTimeSpinner.vue")
		},
		computed: {
			showSeconds() {
				return (this.format || "").indexOf("ss") !== -1;
			},

			offset() {
				return this.showSeconds ? 11 : 8;
			},

			spinner() {
				return this.selectionRange[0] < this.offset
					? this.$refs.minSpinner
					: this.$refs.maxSpinner;
			},

			btnDisabled() {
				return this.minDate.getTime() > this.maxDate.getTime();
			},
			amPmMode() {
				if ((this.format || "").indexOf("A") !== -1) return "A";
				if ((this.format || "").indexOf("a") !== -1) return "a";
				return "";
			}
		},

		data() {
			return {
				popperClass: "",
				minDate: new Date(),
				maxDate: new Date(),
				value: [],
				oldValue: [new Date(), new Date()],
				defaultValue: null,
				format: "HH:mm:ss",
				visible: false,
				selectionRange: [0, 2],
				arrowControl: false
			};
		},

		watch: {
			value(value) {
				if (Array.isArray(value)) {
					this.minDate = new Date(value[0]);
					this.maxDate = new Date(value[1]);
				} else {
					if (Array.isArray(this.defaultValue)) {
						this.minDate = new Date(this.defaultValue[0]);
						this.maxDate = new Date(this.defaultValue[1]);
					} else if (this.defaultValue) {
						this.minDate = new Date(this.defaultValue);
						this.maxDate = advanceTime(new Date(this.defaultValue), 60 * 60 * 1000);
					} else {
						this.minDate = new Date();
						this.maxDate = advanceTime(new Date(), 60 * 60 * 1000);
					}
				}
			},

			async visible(val) {
				if (val) {
					await _.$ensure(() => this.$refs.minSpinner);
					this.oldValue = this.value;
					this.$nextTick(() => this.$refs.minSpinner.emitSelectRange("hours"));
				}
			}
		},

		methods: {
			handleClear() {
				this.$emit("pick", null);
			},

			handleCancel() {
				this.$emit("pick", this.oldValue);
			},

			handleMinChange(date) {
				this.minDate = clearMilliseconds(date);
				this.handleChange();
			},

			handleMaxChange(date) {
				this.maxDate = clearMilliseconds(date);
				this.handleChange();
			},

			handleChange() {
				if (this.isValidValue([this.minDate, this.maxDate])) {
					this.$refs.minSpinner.selectableRange = [
						[minTimeOfDay(this.minDate), this.maxDate]
					];

					this.$refs.maxSpinner.selectableRange = [
						[this.minDate, maxTimeOfDay(this.maxDate)]
					];

					this.$emit("pick", [this.minDate, this.maxDate], true);
				}
			},

			setMinSelectionRange(start, end) {
				this.$emit("select-range", start, end, "min");
				this.selectionRange = [start, end];
			},

			setMaxSelectionRange(start, end) {
				this.$emit("select-range", start, end, "max");
				this.selectionRange = [start + this.offset, end + this.offset];
			},

			handleConfirm(visible = false) {
				const minSelectableRange = this.$refs.minSpinner.selectableRange;
				const maxSelectableRange = this.$refs.maxSpinner.selectableRange;

				this.minDate = limitTimeRange(this.minDate, minSelectableRange, this.format);
				this.maxDate = limitTimeRange(this.maxDate, maxSelectableRange, this.format);

				this.$emit("pick", [this.minDate, this.maxDate], visible);
			},

			async adjustSpinners() {
				await _.$ensure(() => this.$refs.maxSpinner && this.$refs.minSpinner);
				this.$refs.minSpinner.adjustSpinners();
				this.$refs.maxSpinner.adjustSpinners();
			},

			changeSelectionRange(step) {
				const list = this.showSeconds ? [0, 3, 6, 11, 14, 17] : [0, 3, 8, 11];
				const mapping = ["hours", "minutes"].concat(this.showSeconds ? ["seconds"] : []);
				const index = list.indexOf(this.selectionRange[0]);
				const next = (index + step + list.length) % list.length;
				const half = list.length / 2;
				if (next < half) {
					this.$refs.minSpinner.emitSelectRange(mapping[next]);
				} else {
					this.$refs.maxSpinner.emitSelectRange(mapping[next - half]);
				}
			},

			isValidValue(date) {
				return (
					Array.isArray(date) &&
					timeWithinRange(this.minDate, this.$refs.minSpinner.selectableRange) &&
					timeWithinRange(this.maxDate, this.$refs.maxSpinner.selectableRange)
				);
			},

			handleKeydown(event) {
				const keyCode = event.keyCode;
				const mapping = { 38: -1, 40: 1, 37: -1, 39: 1 };

				// Left or Right
				if (keyCode === 37 || keyCode === 39) {
					const step = mapping[keyCode];
					this.changeSelectionRange(step);
					event.preventDefault();
					return;
				}

				// Up or Down
				if (keyCode === 38 || keyCode === 40) {
					const step = mapping[keyCode];
					this.spinner.scrollDown(step);
					event.preventDefault();
					return;
				}
			}
		}
	});
}
</script>
